package des.yordan.mihaylov;

import java.util.LinkedList;

/**
 * @author Yordan Mihaylov
 * @version 2011/06/07
 */

public class SimState {

	public static final int sec_prescaler = 100; // 1s corresponds to 100 ticks
	// flags forcing the RNGs to deliver D-distributed "random" numbers
	private boolean degenerateDistr_service_flag = false;
	private boolean degenerateDistr_arrival_flag = false;

	private EventChain ec;
	private long now; // clock
	private boolean stop; // end simulation
	private boolean serverBusy; // is the server idle

	private long simulationDuration;

	private Customer customerInServer; // current customer

	private LinkedList<Customer> waitingQueue; // waiting customers in FIFO
												// order
	private RandVar iat; // random number generator for inter arrival time
	private RandVar sct; // random number generator for the service completion
							// time
	private double arrivalRate;
	private double serviceRate;
	private long max_WQL;

	DCounter waitingQueueLength;
	DCounter waitingTime;
	DCounter processingTime;
	DCounter retentionTime;
	DCounter customerBlocked; // counts the number of blocked customers and
								// calculates the blocking probability
	DCounter customersServed; // counts the number of all customers served
	TDCounter utilization; // collects the state of the server - time based

	public SimState(double arrivalRate, double serviceRate,
			long simulationDuration, long max_WQL) {
		ec = new EventChain();
		waitingQueue = new LinkedList<Customer>();
		now = 0;
		stop = false;
		serverBusy = false;

		iat = new RandVar(100);
		sct = new RandVar(200);

		this.arrivalRate = arrivalRate;
		this.serviceRate = serviceRate;

		this.max_WQL = max_WQL;

		this.simulationDuration = simulationDuration * sec_prescaler;

		waitingQueueLength = new DCounter();
		waitingTime = new DCounter();
		processingTime = new DCounter();
		retentionTime = new DCounter();
		customerBlocked = new DCounter();
		utilization = new TDCounter();
		customersServed = new DCounter();
	}

	public long getArrivalTime() {
		if (!degenerateDistr_arrival_flag)
			return iat.getLongRV(arrivalRate);
		else
			return (long) arrivalRate;
	}

	public long getServiceTime() {
		if (!degenerateDistr_service_flag)
			return sct.getLongRV(serviceRate);
		else
			return (long) serviceRate;
	}

	public long getQueueSize() {
		return waitingQueue.size();
	}

	public void enqueueCustomer(Customer c) {
		waitingQueue.offer(c);
	}

	public Customer getCustomerInServer() {
		return customerInServer;
	}

	public void setCustomerInServer(Customer customerInServer) {
		this.customerInServer = customerInServer;
	}

	public void nextCustomer(long serviceCompletionTime) {
		customerInServer = getNextCustomer();
		if (null != customerInServer) {
			customerInServer.setServiceInitTime(now);
			customerInServer.setServiceCompletionTime(serviceCompletionTime);
		}
	}

	public void calcCustomerStats() {
		if (null == customerInServer)
			return;

		// waiting time
		long wt = customerInServer.getServiceInitTime()
				- customerInServer.getArrivalTime();
		waitingTime.count(wt);
		// processing time
		long pt = customerInServer.getServiceCompletionTime()
				- customerInServer.getServiceInitTime();
		processingTime.count(pt);
		// retention time
		long rt = wt + pt;
		retentionTime.count(rt);

		customersServed.count(1.);
	}

	// may return NULL if the queue is empty
	public Customer getNextCustomer() {
		return waitingQueue.poll();
	}

	public void enqueueEvent(SimEvent e) {
		ec.addEvent(e);
	}

	// may return NULL if the queue is empty
	public SimEvent getNextEvent() {

		SimEvent e = ec.getNextEvent();
		return e;
	}

	public EventChain getEc() {
		return ec;
	}

	public void setEc(EventChain ec) {
		this.ec = ec;
	}

	public long getNow() {
		return now;
	}

	public void setNow(long time) {
		now = time;
	}

	public boolean isStop() {
		return stop;
	}

	public void setStop(boolean stop) {
		this.stop = stop;
	}

	public boolean isQueueEmpty() {

		return waitingQueue.isEmpty();
	}

	public boolean isServerBusy() {
		return serverBusy;
	}

	public void setServerBusy(boolean serverBusy) {
		this.serverBusy = serverBusy;
	}

	public long getSimulationDuration() {
		return simulationDuration;
	}

	public void setSimulationDuration(long simulationDuration) {
		this.simulationDuration = simulationDuration;
	}

	public long getMax_WQL() {
		return max_WQL;
	}

	public void setDegenerateDistr_service_flag(
			boolean degenerateDistr_service_flag) {
		this.degenerateDistr_service_flag = degenerateDistr_service_flag;
	}

	public void setDegenerateDistr_arrival_flag(
			boolean degenerateDistr_arrival_flag) {
		this.degenerateDistr_arrival_flag = degenerateDistr_arrival_flag;
	}

	public void printReport() {

		// average customer waiting time
		double wt = waitingTime.getMean();
		// average processing time for a customer
		double pt = processingTime.getMean();
		// average retention time for a customer
		double rt = retentionTime.getMean();
		// average number of packets in the queue - Little's Theorem
		double wql = arrivalRate * rt / sec_prescaler;
		// double wql = waitingQueueLength.getMean();
		double minQ = waitingQueueLength.getMin();
		double maxQ = waitingQueueLength.getMax();
		// customer blocking probability
		double cbp = customerBlocked.sumPowerOne / customersServed.sumPowerOne;
		// utilization of the system
		double util = 1 - (utilization.sumPowerOne / now);

		System.out
				.println("\n===================== ----- 1s = 100 ticks ----- ========================\n"
						+ "now = \t\t\t\t\t\t"
						+ (long) (now / sec_prescaler)
						+ "s\n\naverage customer waiting time\t\t\t"
						+ wt
						+ "\naverage processing time for a customer\t\t"
						+ pt
						+ "\naverage retention time for a customer\t\t"
						+ rt
						+ "\nmin number of packets in the queue\t\t"
						+ minQ
						+ "\nmax number of packets in the queue\t\t"
						+ maxQ
						+ "\naverage number of packets in the queue\t\t"
						+ wql
						+ "\ncustomer blocking probability\t\t\t"
						+ cbp
						+ "\nutilization of the system\t\t\t"
						+ util
						+ "\n==========================================================================\n");
	}
}
